import os
import re
import subprocess
import tempfile
import uuid
from typing import Dict, List, Any, Optional, Tuple
from dataclasses import dataclass
from PIL import Image, ImageDraw, ImageFont
from src.database.dao_factory import DAOFactory
from src.utils.logger import log_info, log_error, log_warning


@dataclass
class FootballMatchInfo:
    """足球比赛信息，从字幕中提取"""
    start_time: float  # 字幕开始时间（秒）
    end_time: float    # 字幕结束时间（秒）
    match_image: Optional[str] = None  # 比赛图片路径
    team1_image: Optional[str] = None  # 队伍1图片路径
    team2_image: Optional[str] = None  # 队伍2图片路径
    score1: Optional[int] = None       # 队伍1比分
    score2: Optional[int] = None       # 队伍2比分
    original_text: str = ""            # 原始字幕文本


class FootballIconVideoGenerator:
    """足球图标视频生成器，将图标叠加到视频上"""
    
    def __init__(self, icons_base_dir: str):
        """
        初始化足球图标视频生成器
        
        Args:
            icons_base_dir: 图标文件的基础目录
        """
        self.icons_base_dir = icons_base_dir
        self.dao_factory = DAOFactory.get_instance()
        
        # 确保目录存在
        os.makedirs(os.path.join(self.icons_base_dir, "match"), exist_ok=True)
        os.makedirs(os.path.join(self.icons_base_dir, "team"), exist_ok=True)
        
        # 用于隐藏命令行窗口
        self.startupinfo = None
        if os.name == 'nt':
            self.startupinfo = subprocess.STARTUPINFO()
            self.startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
    
    def _parse_srt_file(self, srt_path: str) -> List[FootballMatchInfo]:
        """
        解析SRT文件，提取足球比赛信息
        新格式: [football_icon:team:afc-wimbledon.png:3][football_icon:team:arsenal-women.png:8][football_icon:match:LaLiga.png]
        
        Args:
            srt_path: SRT字幕文件路径
            
        Returns:
            包含足球比赛信息的列表
        """
        if not os.path.exists(srt_path):
            raise FileNotFoundError(f"SRT文件不存在: {srt_path}")
        
        result = []
        
        with open(srt_path, 'r', encoding='utf-8') as file:
            content = file.read()
        
        # 分割为字幕条目
        # SRT格式: 序号\n时间码\n字幕内容\n空行
        subtitle_blocks = re.split(r'\n\s*\n', content.strip())
        
        for block in subtitle_blocks:
            lines = block.strip().split('\n')
            if len(lines) < 3:
                continue  # 跳过格式不正确的字幕块

            # 解析时间码
            time_line = lines[1]
            time_match = re.search(r'(\d{2}:\d{2}:\d{2},\d{3})\s*-->\s*(\d{2}:\d{2}:\d{2},\d{3})', time_line)
            if not time_match:
                continue
                
            start_time = self._convert_srt_time_to_seconds(time_match.group(1))
            end_time = self._convert_srt_time_to_seconds(time_match.group(2))
            
            # 获取字幕文本内容
            text = '\n'.join(lines[2:])
            
            # 创建比赛信息对象
            match_info = FootballMatchInfo(
                start_time=start_time,
                end_time=end_time,
                original_text=text
            )
            
            # 提取新格式标记
            # [football_icon:type:filename:score] 格式
            # 新格式: [football_icon:type:file_id:filename:score]，其中file_id为数字
            icon_pattern = r'\[football_icon:([^:]+):(\d+):([^:\]]+)(?::(\d+))?\]'
            icons = re.findall(icon_pattern, text)
            
            # 用于跟踪团队图标数量
            team_count = 0
            uploaded_file_dao = self.dao_factory.get_uploaded_file_dao()
            for icon_type,file_id, filename, score in icons:
                if icon_type == 'match':
                    match_info.match_image = uploaded_file_dao.get_by_id(file_id).file_path
                elif icon_type == 'team':
                    # 第一个team是team1，第二个是team2
                    if team_count == 0:
                        match_info.team1_image = uploaded_file_dao.get_by_id(file_id).file_path
                        match_info.team1_name = filename  # 保存队伍名称
                        if score:  # 如果分数存在
                            match_info.score1 = int(score)
                        team_count += 1
                    elif team_count == 1:
                        match_info.team2_image = uploaded_file_dao.get_by_id(file_id).file_path
                        match_info.team2_name = filename  # 保存队伍名称
                        if score:  # 如果分数存在
                            match_info.score2 = int(score)
                        team_count += 1
            # 只有当至少有一个比赛元素被提取出来时才添加
            if match_info.match_image or match_info.team1_image or match_info.team2_image:
                result.append(match_info)

        return result
    
    def _convert_srt_time_to_seconds(self, time_str: str) -> float:
        """将SRT时间格式转换为秒数"""
        hours, minutes, seconds = time_str.split(':')
        seconds, milliseconds = seconds.split(',')
        return (int(hours) * 3600 + 
                int(minutes) * 60 + 
                int(seconds) + 
                int(milliseconds) / 1000)
    
    def _get_video_info(self, video_path: str) -> Dict[str, Any]:
        """获取视频基本信息"""
        try:
            # 获取视频分辨率
            cmd = [
                "ffprobe", 
                "-v", "error", 
                "-select_streams", "v:0", 
                "-show_entries", "stream=width,height", 
                "-of", "csv=p=0", 
                video_path
            ]
            
            output = subprocess.check_output(cmd, startupinfo=self.startupinfo).decode().strip()
            width, height = map(int, output.split(','))
            
            # 获取视频时长
            cmd = [
                "ffprobe", 
                "-v", "error", 
                "-show_entries", "format=duration", 
                "-of", "csv=p=0", 
                video_path
            ]
            
            duration = float(subprocess.check_output(cmd, startupinfo=self.startupinfo).decode().strip())
            
            return {
                "success": True,
                "width": width,
                "height": height,
                "duration": duration
            }
        except Exception as e:
            log_error(f"获取视频信息失败: {e}")
            return {"success": False, "message": str(e)}
    
    def _create_score_image(self, score1: Optional[int] = None, score2: Optional[int] = None, width: int = 400, height: int = 120) -> str:
        """
        创建比分图像 - 使用白色加粗字体，无背景，无描边
        
        Args:
            score1: 队伍1比分，如果为None则显示X
            score2: 队伍2比分，如果为None则显示X
            width: 图像宽度
            height: 图像高度
            
        Returns:
            临时比分图像的文件路径
        """
        # 创建透明背景的图像
        image = Image.new('RGBA', (width, height), (0, 0, 0, 0))
        draw = ImageDraw.Draw(image)
        
        font = ImageFont.truetype("arialbd.ttf", 60)  # 更大的字体

    
        # 确定显示文本 - 如果两个分数都是None或默认为X，则显示VS
        score_text = ""
        score1_text = "X" if score1 is None else str(score1)
        score2_text = "X" if score2 is None else str(score2)
        
        if score1_text == "X" and score2_text == "X":
            score_text = "VS"
        else:
            score_text = f"{score1_text} : {score2_text}"
        
        # 计算文本宽度
        text_width = draw.textlength(score_text, font=font) if hasattr(draw, 'textlength') else font.getlength(score_text)
        text_x = (width - text_width) // 2
        text_y = (height - 60) // 2  # 基于字体大小调整垂直位置
        
        # 直接绘制白色文本，不加描边
        draw.text((text_x, text_y), score_text, font=font, fill=(255, 255, 255, 255))  # 改为白色
    
        # 保存到临时文件
        temp_file = os.path.join(tempfile.gettempdir(), f"score_{uuid.uuid4()}.png")
        image.save(temp_file, "PNG")
        
        return temp_file
    
    def _create_team_name_image(self, team_name: str, width: int = 300, height: int = 60) -> str:
        """
        创建队伍名称图像 - 使用白色字体，无背景
        
        Args:
            team_name: 队伍名称
            width: 图像宽度
            height: 图像高度
            
        Returns:
            临时队伍名称图像的文件路径
        """
        # 创建透明背景的图像
        image = Image.new('RGBA', (width, height), (0, 0, 0, 0))
        draw = ImageDraw.Draw(image)
        
        # 尝试加载字体，如果失败则使用默认字体
        font = ImageFont.truetype("arialbd.ttf", 30)  # 30px字体
    
        # 计算文本宽度
        text_width = draw.textlength(team_name, font=font) if hasattr(draw, 'textlength') else font.getlength(team_name)
        text_x = (width - text_width) // 2
        text_y = (height - 30) // 2  # 基于字体大小调整垂直位置
        
        # 直接绘制白色文本
        draw.text((text_x, text_y), team_name, font=font, fill=(255, 255, 255, 255))
        
        # 保存到临时文件
        temp_file = os.path.join(tempfile.gettempdir(), f"teamname_{uuid.uuid4()}.png")
        image.save(temp_file, "PNG")
        
        return temp_file
    
    def generate_video_with_icons(self, video_path: str, match_info_list: List[FootballMatchInfo], output_path: Optional[str] = None) -> Dict[str, Any]:
        """
        生成带有图标的视频
        
        Args:
            video_path: 视频文件路径
            match_info_list: 比赛信息列表
            output_path: 输出视频路径，如果为None则自动生成
        
        Returns:
            处理结果字典
        """
        if not os.path.exists(video_path):
            return {"success": False, "message": f"视频文件不存在: {video_path}"}
        
        if not match_info_list:
            return {"success": False, "message": "没有找到匹配的比赛信息"}
        
        # 如果未提供输出路径，生成一个
        if output_path is None:
            dirname = os.path.dirname(video_path)
            basename = os.path.basename(video_path)
            name, ext = os.path.splitext(basename)
            output_path = os.path.join(dirname, f"{name}_with_icons{ext}")
        
        # 获取视频信息
        video_info = self._get_video_info(video_path)
        if not video_info["success"]:
            return {"success": False, "message": f"无法获取视频信息: {video_info['message']}"}
        
        # 计算图标大小 - 视频宽度的18%
        video_width = video_info["width"]
        icon_width = int(video_width * 0.18)  # 改为18%而不是1/4
        
        # 临时文件列表，用于清理
        temp_files = []
        
        try:
            # 为每个比赛信息准备过滤器
            filter_complex = []
            input_files = [video_path]  # 视频是第一个输入
            
            # 构建过滤器串联表达式
            last_video = "[0:v]"
            vindex = 1
            
            for i, info in enumerate(match_info_list):
                # 计算图标相关尺寸
                match_height = icon_width // 2  # 估算比赛图标高度
                team_height = icon_width // 2   # 估算队伍图标高度
                match_y = f"80"  # 距顶部80像素
                
                # 验证比赛图片文件是否存在
                if info.match_image and os.path.exists(info.match_image):
                    input_files.append(info.match_image)
                    # 计算X、Y坐标 (顶部中间)
                    match_x = f"(W-w)/2"
                    
                    # 使用固定宽度缩放比赛图标到视频宽度的18%
                    filter_complex.append(
                        f"[{vindex}:v]scale={icon_width}:-1[scaled{vindex}];"
                        f"[scaled{vindex}]format=rgba[fmt{vindex}];"
                        f"{last_video}[fmt{vindex}]overlay={match_x}:{match_y}"
                        f":enable='between(t,{info.start_time},{info.end_time})'[v{i}_match]"
                    )
                    last_video = f"[v{i}_match]"
                    vindex += 1
                    
                    # 有比赛图标时，队伍图标在其下方
                    team_row_y = f"{match_y}+{match_height}+60"
                else:
                    # 无比赛图标时，队伍图标直接放在顶部，位置稍低一些
                    team_row_y = f"160"
                    if info.match_image:
                        log_warning(f"比赛图片不存在: {info.match_image}")
        
                # 添加队伍1图片 - 放在左侧
                if info.team1_image and os.path.exists(info.team1_image):
                    input_files.append(info.team1_image)
                    # 计算X、Y坐标
                    team1_x = f"W*0.25-w/2"  # 屏幕宽度的1/4位置
                    
                    # 使用固定宽度缩放队伍图标到视频宽度的18%
                    filter_complex.append(
                        f"[{vindex}:v]scale={icon_width}:-1[scaled{vindex}];"
                        f"[scaled{vindex}]format=rgba[fmt{vindex}];"
                        f"{last_video}[fmt{vindex}]overlay={team1_x}:{team_row_y}"
                        f":enable='between(t,{info.start_time},{info.end_time})'[v{i}_team1]"
                    )
                    last_video = f"[v{i}_team1]"
                    vindex += 1
                    
                    # 添加队伍1名称 - 放在队伍1图标下方15px处
                    if hasattr(info, 'team1_name') and info.team1_name:
                        team1_name_image = self._create_team_name_image(info.team1_name, width=300)
                        temp_files.append(team1_name_image)
                        input_files.append(team1_name_image)
                        
                        # 计算X、Y坐标 - 与队伍图标水平居中，垂直放置在图标下方15px
                        team1_name_x = f"W*0.25-w/2"  # 与队伍图标相同的X坐标
                        team1_name_y = f"{team_row_y}+{team_height}+80"  # 队伍图标下方15px
                        
                        filter_complex.append(
                            f"[{vindex}:v]format=rgba[fmt{vindex}];"
                            f"{last_video}[fmt{vindex}]overlay={team1_name_x}:{team1_name_y}"
                            f":enable='between(t,{info.start_time},{info.end_time})'[v{i}_team1_name]"
                        )
                        last_video = f"[v{i}_team1_name]"
                        vindex += 1
                else:
                    if info.team1_image:
                        log_warning(f"队伍1图片不存在: {info.team1_image}")
            
                # 添加队伍2图片 - 放在右侧
                if info.team2_image and os.path.exists(info.team2_image):
                    input_files.append(info.team2_image)
                    # 计算X、Y坐标
                    team2_x = f"W*0.75-w/2"  # 屏幕宽度的3/4位置
                    
                    # 使用固定宽度缩放队伍图标到视频宽度的18%
                    filter_complex.append(
                        f"[{vindex}:v]scale={icon_width}:-1[scaled{vindex}];"
                        f"[scaled{vindex}]format=rgba[fmt{vindex}];"
                        f"{last_video}[fmt{vindex}]overlay={team2_x}:{team_row_y}"
                        f":enable='between(t,{info.start_time},{info.end_time})'[v{i}_team2]"
                    )
                    last_video = f"[v{i}_team2]"
                    vindex += 1
                    
                    # 添加队伍2名称 - 放在队伍2图标下方15px处
                    if hasattr(info, 'team2_name') and info.team2_name:
                        team2_name_image = self._create_team_name_image(info.team2_name, width=300)
                        temp_files.append(team2_name_image)
                        input_files.append(team2_name_image)
                        
                        # 计算X、Y坐标 - 与队伍图标水平居中，垂直放置在图标下方15px
                        team2_name_x = f"W*0.75-w/2"  # 与队伍图标相同的X坐标
                        team2_name_y = f"{team_row_y}+{team_height}+80"  # 队伍图标下方15px
                        
                        filter_complex.append(
                            f"[{vindex}:v]format=rgba[fmt{vindex}];"
                            f"{last_video}[fmt{vindex}]overlay={team2_name_x}:{team2_name_y}"
                            f":enable='between(t,{info.start_time},{info.end_time})'[v{i}_team2_name]"
                        )
                        last_video = f"[v{i}_team2_name]"
                        vindex += 1
                else:
                    if info.team2_image:
                        log_warning(f"队伍2图片不存在: {info.team2_image}")
            
                # 添加比分图片 - 放在与队伍图标水平居中的位置
                score_image = self._create_score_image(info.score1, info.score2, width=400, height=120)
                temp_files.append(score_image)
                input_files.append(score_image)
                
                # 计算X、Y坐标 - 保持水平居中
                score_x = f"(W-w)/2"  # 水平居中
                score_y = f"{team_row_y}"  # 与调整后的队伍图标在同一高度
                
                # 添加比分图片，不需要缩放
                filter_complex.append(
                    f"[{vindex}:v]format=rgba[fmt{vindex}];"
                    f"{last_video}[fmt{vindex}]overlay={score_x}:{score_y}"
                    f":enable='between(t,{info.start_time},{info.end_time})'[v{i}_score]"
                )
                last_video = f"[v{i}_score]"
                vindex += 1
    
            # 如果没有有效的叠加效果，直接复制视频
            if last_video == "[0:v]":
                return {"success": False, "message": "没有有效的图标可以叠加到视频上"}
            
            # 构建命令
            cmd = ["ffmpeg", "-y"]
            
            # 添加所有输入文件
            for input_file in input_files:
                cmd.extend(["-i", input_file])
            
            # 添加复杂过滤器
            filter_str = ";".join(filter_complex)
            cmd.extend(["-filter_complex", filter_str])
            
            # 设置输出选项
            cmd.extend([
                "-map", last_video,  # 使用最后一个视频流
                "-map", "0:a?",  # 复制所有音频流
                "-c:v", "libx264",
                "-preset", "medium", 
                "-crf", "23",
                "-c:a", "copy",
                output_path
            ])
            
            # 执行命令
            log_info(f"开始添加足球图标到视频: {os.path.basename(video_path)}")
            log_info(f"FFmpeg命令: {' '.join(cmd)}")
            
            process = subprocess.Popen(
                cmd, 
                stdout=subprocess.PIPE, 
                stderr=subprocess.PIPE,
                startupinfo=self.startupinfo,
                universal_newlines=True
            )
            
            _, stderr = process.communicate()
            
            # 检查处理结果
            if process.returncode != 0:
                log_error(f"添加图标失败: {stderr}")
                return {"success": False, "message": "添加图标失败", "error": stderr}
            
            # 验证输出文件
            if os.path.exists(output_path) and os.path.getsize(output_path) > 0:
                log_info(f"图标添加成功: {output_path}")
                return {
                    "success": True,
                    "message": "图标添加成功",
                    "output_path": output_path
                }
            else:
                log_error("输出文件生成失败或为空")
                return {"success": False, "message": "输出文件生成失败或为空"}
            
        except Exception as e:
            log_error(f"处理图标视频时发生错误: {str(e)}")
            import traceback
            log_error(traceback.format_exc())
            return {"success": False, "message": f"处理错误: {str(e)}"}
        
        finally:
            # 清理临时文件
            for temp_file in temp_files:
                try:
                    if os.path.exists(temp_file):
                        os.remove(temp_file)
                except Exception as e:
                    log_warning(f"删除临时文件失败: {e}")

    def parse_and_generate(self, video_paths: List[str], subtitle_path: str) -> Dict[str, Any]:
        """
        解析字幕并生成带有图标的视频
        
        Args:
            video_paths: 视频文件路径列表
            subtitle_path: 字幕文件路径
            
        Returns:
            处理结果字典
        """
        try:
            # 验证输入参数
            if not os.path.exists(subtitle_path):
                raise FileNotFoundError(f"字幕文件不存在: {subtitle_path}")
                
            if not video_paths or not isinstance(video_paths, list):
                raise ValueError("视频路径列表不能为空且必须是列表类型")
            
            # 解析字幕文件，提取比赛信息
            match_info_list = self._parse_srt_file(subtitle_path)
            log_info(f"解析到 {len(match_info_list)} 条比赛信息")
            
            if not match_info_list:
                raise ValueError("在字幕文件中没有找到有效的比赛信息")
            
            # 处理每个视频文件
            results = []
            
            for video_path in video_paths:
                if not os.path.exists(video_path):
                    log_warning(f"视频文件不存在: {video_path}")
                    results.append({
                        "video_path": video_path,
                        "output_path": "",
                        "success": False,
                        "message": "视频文件不存在"
                    })
                    continue
                    
                log_info(f"开始处理视频: {os.path.basename(video_path)}")
                
                # 为每个视频生成带有图标的版本
                result = self.generate_video_with_icons(
                    video_path=video_path,
                    match_info_list=match_info_list
                )
                
                results.append({
                    "video_path": video_path,
                    "output_path": result.get("output_path", ""),
                    "success": result.get("success", False),
                    "message": result.get("message", "")
                })
            
            # 统计成功和失败的数量
            success_count = sum(1 for r in results if r.get("success", False))
            
            return results, success_count
            
        except Exception as e:
            raise Exception(f"解析和生成视频时发生错误: {str(e)}") from e

